/****************************************************************************
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2020 VeriSilicon Holdings Co., Ltd.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 *****************************************************************************
 *
 * The GPL License (GPL)
 *
 * Copyright (c) 2020 VeriSilicon Holdings Co., Ltd.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program;
 *
 *****************************************************************************
 *
 * Note: This software is released under dual MIT and GPL licenses. A
 * recipient may use this file under the terms of either the MIT license or
 * GPL License. If you wish to use only one license not the other, you can
 * indicate your decision by deleting one of the above license notices in your
 * version of this file.
 *
 *****************************************************************************/
#include <linux/version.h>
#include <media/v4l2-subdev.h>

#include "vvbuf.h"

void vvbuf_ctx_init(struct vvbuf_ctx *ctx)
{
	if (unlikely(!ctx))
		return;

	spin_lock_init(&ctx->irqlock);
	INIT_LIST_HEAD(&ctx->dmaqueue);
}

void vvbuf_ctx_deinit(struct vvbuf_ctx *ctx)
{
	/*nop*/
}

struct vb2_dc_buf *vvbuf_pull_buf(struct vvbuf_ctx *ctx)
{
	unsigned long flags;
	struct vb2_dc_buf *buf = NULL;
	if (unlikely(!ctx))
		return NULL;
	spin_lock_irqsave(&ctx->irqlock, flags);

	if (list_empty(&ctx->dmaqueue)) {
		spin_unlock_irqrestore(&ctx->irqlock, flags);
		return NULL;
	}
	buf = list_first_entry(&ctx->dmaqueue, struct vb2_dc_buf, irqlist);
	list_del(&buf->irqlist);

	spin_unlock_irqrestore(&ctx->irqlock, flags);
	return buf;
}

int vvbuf_push_buf(struct vvbuf_ctx *ctx, struct vb2_dc_buf *buf)
{
	unsigned long flags;
	if (unlikely(!ctx))
		return -1;
	if (unlikely(!buf))
		return -1;

	spin_lock_irqsave(&ctx->irqlock, flags);
	list_add_tail(&buf->irqlist, &ctx->dmaqueue);
	spin_unlock_irqrestore(&ctx->irqlock, flags);
	return 0;
}



struct vb2_dc_buf *vvbuf_try_dqbuf(struct vvbuf_ctx *ctx)
{
	struct vb2_dc_buf *buf;
	unsigned long flags;

	if (unlikely(!ctx))
		return NULL;

	spin_lock_irqsave(&ctx->irqlock, flags);
	if (list_empty(&ctx->dmaqueue)) {
		spin_unlock_irqrestore(&ctx->irqlock, flags);
		return NULL;
	}

	buf = list_first_entry(&ctx->dmaqueue, struct vb2_dc_buf, irqlist);
	spin_unlock_irqrestore(&ctx->irqlock, flags);
	return buf;
}

void vvbuf_try_dqbuf_done(struct vvbuf_ctx *ctx, struct vb2_dc_buf *buf)
{
	unsigned long flags;

	if (unlikely(!ctx))
		return;

	spin_lock_irqsave(&ctx->irqlock, flags);
	if (list_empty(&ctx->dmaqueue)) {
		spin_unlock_irqrestore(&ctx->irqlock, flags);
		return;
	}

	if (buf == list_first_entry(&ctx->dmaqueue,
			struct vb2_dc_buf, irqlist))
		list_del(&buf->irqlist);
	spin_unlock_irqrestore(&ctx->irqlock, flags);
}

void vvbuf_ready(struct vvbuf_ctx *ctx, struct media_pad *pad,
				struct vb2_dc_buf *buf)
{
	struct video_device *vdev;
	struct v4l2_subdev *subdev;
	struct vvbuf_ctx *rctx = NULL;

	if (unlikely(!pad || !buf))
		return;

#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 19, 0)
	pad = media_entity_remote_pad(pad);
#else
	pad = media_pad_remote_pad_first(pad);
#endif
	if (!pad)
		return;

	if (is_media_entity_v4l2_video_device(pad->entity)) {
		vdev = media_entity_to_video_device(pad->entity);
		if (vdev)
			rctx = (struct vvbuf_ctx *)video_get_drvdata(vdev);
	} else if (is_media_entity_v4l2_subdev(pad->entity)) {
		subdev = media_entity_to_v4l2_subdev(pad->entity);
		if (subdev)
			rctx = (struct vvbuf_ctx *)v4l2_get_subdevdata(subdev);
	}

	rctx += pad->index;
	buf->pad = pad;

	if (rctx && rctx->ops && rctx->ops->notify)
		rctx->ops->notify(rctx, buf);
}

